package ch.bfh.medinfo.javafx.dice.b_2;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.input.InputEvent;
import ch.bfh.medinfo.javafx.dice.definition.StatisticalDice;
import ch.bfh.medinfo.javafx.dice.implementation.ComposedStatisticalDice;
import ch.bfh.medinfo.javafx.dice.implementation.FairDice;
import ch.bfh.medinfo.javafx.dice.implementation.UnFairDice;

public class DiceController {
	private StatisticalDice dice;

	// Backing-List
	private List<Integer> list;
	// List-Model
	private ObservableList<Integer> observableList;
	private boolean isDicePlaying;
	private Runnable dicePlaying;

	public DiceController() {
		// Careful! No injected instance available here! The injection will only
		// take place after the execution of this constructor!
		// So, if you try to access any variable using an injection, such as
		// listView, you will get a Nullpointer Exception here.

		// Create a new Dice
		this.dice = new ComposedStatisticalDice(new FairDice(1,6));

		// Create the backing-List (Initialize it with the first values)
		list = new ArrayList<>();
		for (int i : dice.getStatistics()) {
			list.add(i);
		}
		// Create the list-Model (using the backing list)
		observableList = FXCollections.observableList(list);

		dicePlaying = new DicePlaying();
		Thread t = new Thread(new DicePlaying());
		t.start();

	}

	@FXML
	void initialize() {
		// Only now, the variables are initialized with the injected instances.
		// So accessing the instance of statisticsView now possible.
		assert labelActualValueId != null : "fx:id=\"labelActualValueId\" was not injected: check your FXML file 'DiceLayout.fxml'.";
		assert statisticsView != null : "fx:id=\"statisticsView\" was not injected: check your FXML file 'DiceLayout.fxml'.";

		// Only now, the statisticsView is injected (instanciated)
		statisticsView.setItems(observableList);
		statisticsView.setFocusModel(null);
	}

	@FXML
	private ResourceBundle resources;

	@FXML
	private URL location;

	@FXML
	private Label labelActualValueId;

	@FXML
	private ListView<Integer> statisticsView;

	@FXML
	void playStart(InputEvent event) {
		// Or if inputEvent is known: (ActionEvent event)
		synchronized (dicePlaying) {
			isDicePlaying = true;
			dicePlaying.notifyAll();
		}
	}

	@FXML
	void action(ActionEvent event) {
		System.out.println(event);
	}

	@FXML
	void playStop(InputEvent event) {
		// Or if inputEvent is known: (MouseEvent event)
		synchronized (dicePlaying) {
			isDicePlaying = false;
			dicePlaying.notifyAll();
		}

	}

	class DicePlaying implements Runnable {
		final int MAX_DELAY=250;
		final int MIN_DELAY=2;
		final int DELAY_STEP=10;
		final Runnable diceUpdater = new Runnable() {
			@Override
			public void run() {
				int count = 0;
				for (int i : dice.getStatistics()) {
					if (observableList.get(count) != i)
						observableList.set(count, i);
					count++;
				}
				labelActualValueId.setText("" + dice.getActualValue());
			}
		};

		@Override
		public void run() {
			boolean playing = false;
			int delay = MAX_DELAY;
			while (!Thread.currentThread().isInterrupted()) {
				synchronized (dicePlaying) {
					playing = isDicePlaying;
				}
				if (!playing) {
					synchronized (dicePlaying) {
						try {
							dicePlaying.wait();
							delay = MAX_DELAY;
						} catch (InterruptedException e) {
							Thread.currentThread().interrupt();
						}
					}
				}
				if (playing) {
					synchronized (dicePlaying) {
						dice.play();
						// It is important to update the text of the label on
						// the JavaFX-Application-Thread.
						Platform.runLater(diceUpdater);

					}
					try {
						Thread.sleep(delay);
						if (delay > MIN_DELAY)
							delay -= DELAY_STEP;
						else
							delay=MIN_DELAY;

					} catch (InterruptedException e) {
						Thread.currentThread().interrupt();
					}
				}

			}

		}

	}

}
